package com.whmall.service.crm.module.customerpool.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.whmall.common.enumeration.VerifyEnum;
import com.whmall.common.enumeration.YesNoEnum;
import com.whmall.component.entity.dto.*;
import com.whmall.component.enumeration.*;
import com.whmall.component.im.constant.NoticeCategoryConstant;
import com.whmall.component.im.constant.NoticeContentTypeConstant;
import com.whmall.component.im.constant.NoticeOperationTypeConstant;
import com.whmall.component.im.entity.EmployeeNoticeDTO;
import com.whmall.component.util.BeanUtilsWhmall;
import com.whmall.component.util.DateUtils;
import com.whmall.component.util.FieldUtils;
import com.whmall.service.common.api.MessageApi;
import com.whmall.service.crm.algorithm.CustomerPoolAlgorithm;
import com.whmall.service.crm.constant.CompanyConstant;
import com.whmall.service.crm.entity.dto.company.CompanyTypeRspDTO;
import com.whmall.service.crm.entity.dto.customerpool.*;
import com.whmall.service.crm.enumeration.CustomerFollowStatusEnum;
import com.whmall.service.crm.enumeration.CustomerPoolOperationTypeEnum;
import com.whmall.service.crm.module.company.dao.CompanyInformationMapper;
import com.whmall.service.crm.module.company.dao.CompanyLogMapper;
import com.whmall.service.crm.module.company.dao.CompanyMapper;
import com.whmall.service.crm.module.company.entity.CompanyDO;
import com.whmall.service.crm.module.company.entity.CompanyInformationDO;
import com.whmall.service.crm.module.company.entity.CompanyLogDO;
import com.whmall.service.crm.module.company.entity.qo.CompanyQO;
import com.whmall.service.crm.module.company.service.CompanyAlgorithm;
import com.whmall.service.crm.module.company.service.CompanyIndustryTypeService;
import com.whmall.service.crm.module.customerpool.dao.CustomerPoolFollowMapper;
import com.whmall.service.crm.module.customerpool.dao.CustomerPoolFollowRecordMapper;
import com.whmall.service.crm.module.customerpool.dao.CustomerPoolLogMapper;
import com.whmall.service.crm.module.customerpool.entity.CustomerPoolFollowDO;
import com.whmall.service.crm.module.customerpool.entity.CustomerPoolFollowRecordDO;
import com.whmall.service.crm.module.customerpool.entity.CustomerPoolLogDO;
import com.whmall.service.crm.module.customerpool.entity.qo.CustomerPoolFollowQO;
import com.whmall.service.crm.module.customerpool.entity.qo.CustomerPoolFollowRecordQO;
import com.whmall.service.crm.module.customerpool.entity.qo.CustomerPoolLogQO;
import com.whmall.service.crm.module.customerpool.service.CustomerPoolService;
import com.whmall.service.crm.util.PageDTOHelper;
import com.whmall.service.system.api.EmployeeApi;
import com.whmall.service.system.entity.dto.employee.EmployeeRspDTO;
import com.whmall.service.system.module.employee.enumeration.EmployeeStatusEnum;
import com.whmall.service.system.module.employee.enumeration.PositionEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class CustomerPoolServiceImpl implements CustomerPoolService {

    private final CompanyMapper companyMapper;
    private final CompanyInformationMapper companyInformationMapper;
    private final CustomerPoolLogMapper customerPoolLogMapper;
    private final CompanyLogMapper companyLogMapper;
    private final CustomerPoolFollowMapper customerPoolFollowMapper;
    private final CustomerPoolFollowRecordMapper customerPoolFollowRecordMapper;
    private final CustomerPoolAlgorithm customerPoolAlgorithm;
    private final CompanyAlgorithm companyAlgorithm;
    @DubboReference
    private MessageApi messageApi;
    @DubboReference
    private EmployeeApi employeeApi;
    private final CompanyIndustryTypeService companyIndustryTypeService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void noOrderWithinTimeLimitJobHandler(Date followTime, Date effectiveTime) {
        List<CompanyDO> companies = companyMapper.listBy(
                CompanyQO.builder().isCustomerPoolCustomer(0)
                        // 防止新注册未审核用户进入开发客户栏
                        .verified(VerifyEnum.YES.getValue())
                        .deleted(0)
                        .lastOrderTimeLt(DateUtils.addDays(DateUtils.getDayBegin(new Date()),
                                (-1) * CompanyConstant.CUSTOMER_POOL_NO_ORDER_DAYS))
                        // 锁定的不进入公告池
                        .isLockPersonalPoolCustomer(0)
                        // 过滤内部公司
                        .isInternal(0)
                        // 网化商城所有的公共池客户 全部平均分配撮合为杨红艳/许怡然 并且撮合是他们的公司不参与释放逻辑 75天未下单不进入个人公共池待跟进
                        .traderIdNotList(Arrays.asList(516L, 465L))
                        // 不是注销的
                        .isLogout(0)
                        .build());
        if (companies.isEmpty()) {
            return;
        }
        // 查询公司行业类型
        log.info("公共池定时任务，修改公司ID：{}", CollUtil.join(companies.stream().map(CompanyDO::getId).collect(Collectors.toList()), ","));
        //进入开发客户栏
        companies = companies.stream()
                .filter(e -> {
                    // 没有撮合的不要
                    if (e.getTraderId().equals(0L)) {
                        return false;
                    }
                    // 大客户不要
                    return !e.getIsBigCustomer().equals(1);
                }).collect(Collectors.toList());
        List<Long> companyIds = companies.stream().map(CompanyDO::getId).collect(Collectors.toList());
        log.info("公共池定时任务，修改公司ID：{}", CollUtil.join(companyIds, ","));
        if (companyIds.isEmpty()) {
            return;
        }
        companies.forEach(e -> {
            CustomerPoolFollowDO follow = CustomerPoolFollowDO.builder().sourceId(0L)
                    .companyId(e.getId())
                    .traderId(e.getTraderId())
                    .traderDepartmentPath(e.getTraderDepartmentPath())
                    .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                    .followTime(followTime)
                    .effectiveTime(effectiveTime)
                    .createTime(new Date()).build();
            customerPoolFollowMapper.insertSelective(follow);
            customerPoolFollowMapper.updateByIdSelective(
                    CustomerPoolFollowDO.builder().id(follow.getId()).rootId(follow.getId()).build()
            );
            CustomerPoolFollowRecordDO record = new CustomerPoolFollowRecordDO();
            BeanUtils.copyProperties(follow, record);
            record.setId(null);
            record.setFollowId(follow.getId());
            record.setCreateTime(new Date());
            // 如果最后下单时间是默认时间，则为新公司
            record.setRemark(CompanyConstant.CUSTOMER_POOL_NO_ORDER_DAYS + "天未下单进入个人公共池");
            customerPoolFollowRecordMapper.insert(record);
            //记录日志
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(e.getId())
                            .operationType(CustomerPoolOperationTypeEnum.ENTER_CUSTOMER_POOL.getValue())
                            .remark(record.getRemark())
                            .handleType(OperatorTypeEnum.SYSTEM.getValue())
                            .handleId(0L)
                            .handleTime(new Date()).build()
            );

            //添加公司操作日志
            CompanyLogDO logDO = CompanyLogDO.builder()
                    .companyId(e.getId())
                    .createType(OperatorTypeEnum.SYSTEM.getValue())
                    .createId(0L)
                    .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                    .operationDesc1(CompanyConstant.CUSTOMER_POOL_NO_ORDER_DAYS + "天未下单进入个人公共池")
                    .ip(null).createTime(new Date()).build();
            companyLogMapper.insertSelective(logDO);
        });
        companyMapper.updateByIds(
                CompanyQO.builder()
                        .ids(companyIds)
                        .isCustomerPoolCustomer(1)
                        .updateTime(new Date())
                        .build());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void releaseCustomerJobHandler(Map<Long, QueryManagerRspDTO> traders, Map<Long, Date> employeeNotices) {
        //1、释放客户-超时未跟进的
        List<CustomerPoolFollowDO> follows = customerPoolFollowMapper.selectBy(
                CustomerPoolFollowQO.builder()
                        //加一点时间防止系统时间不准
                        .effectiveTimeLte(DateUtils.addSeconds(new Date(), 60))
                        .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                        .build());
        log.debug("此次可能释放：{}", CollUtil.join(follows.stream().map(CustomerPoolFollowDO::getId).collect(Collectors.toList()), ","));
        if (CollUtil.isEmpty(follows)) {
            return;
        }

        List<Long> companyIdList = follows.stream().map(CustomerPoolFollowDO::getCompanyId).collect(Collectors.toList());

        //高校研发机构不参与释放规则
        Set<Long> companyIdsCanRelease = !follows.isEmpty() ? companyMapper.getByIds(companyIdList)
                .stream()
                .filter(e -> {
                    // 锁定的，不要
                    if (e.getIsLockPersonalPoolCustomer().equals(1)) {
                        return false;
                    }
                    // 无开发价值，不要
                    return !e.getIsDevelopmentValue().equals(0);
                })
                .map(CompanyDO::getId).collect(Collectors.toSet())
                : Collections.emptySet();

        log.debug("此次可以释放的公司：{}", CollUtil.join(companyIdsCanRelease, ","));
        follows.stream().filter(e -> companyIdsCanRelease.contains(e.getCompanyId())).forEach(e -> {
            customerPoolFollowMapper.updateByIdSelective(
                    CustomerPoolFollowDO.builder().id(e.getId())
                            .followStatus(CustomerFollowStatusEnum.RELEASED.getValue())
                            .updateTime(new Date()).build()
            );
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(e.getCompanyId())
                            .operationType(CustomerPoolOperationTypeEnum.RELEASE_CUSTOMER.getValue())
                            .remark("超时未跟进系统自动释放")
                            .handleType(OperatorTypeEnum.SYSTEM.getValue())
                            .handleId(0L)
                            .handleTime(new Date()).build()
            );
            //公司日志
            CompanyLogDO logDO = CompanyLogDO.builder()
                    .companyId(e.getCompanyId())
                    .createType(OperatorTypeEnum.SYSTEM.getValue())
                    .createId(0L)
                    .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                    .operationDesc1("释放公司，原因“超时未跟进系统自动释放”")
                    .ip(null).createTime(new Date()).build();
            companyLogMapper.insertSelective(logDO);
        });
        if (!companyIdsCanRelease.isEmpty()) {
            Map<Long, CompanyDO> companyDOMap = companyMapper.getByIds(new ArrayList<>(companyIdsCanRelease)).stream().collect(Collectors.toMap(CompanyDO::getId, e -> e));


            List<QueryManagerRspDTO> queryManagerRspDTOS = employeeApi.queryManagers(QueryManagerReqDTO.builder().build());
            List<Long> employees = queryManagerRspDTOS.stream().filter(e ->
                    e.getPower().equals(PowerEnum.TRADER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && e.getIsCaocno()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            Long traderId = companyAlgorithm.getOneManager(PositionEnum.TRADER.getValue(), false, null, employees);
            EmployeeRspDTO employeeRspDTO = employeeApi.getById(traderId);
            Assert.notNull(employeeRspDTO, "员工" + traderId + "不存在");

            companyMapper.updateByIds(
                    CompanyQO.builder().ids(new ArrayList<>(companyIdsCanRelease))
                            .traderId(employeeRspDTO.getId())
                            .traderDepartmentPath(employeeRspDTO.getDepartmentPath())
                            .isLockPersonalPoolCustomer(0)
                            .updateTime(new Date()).build()
            );
            companyIdsCanRelease.forEach(id -> {
                CompanyDO companyDO = companyDOMap.get(id);
                if (companyDO != null) {
                    CustomerPoolAlgorithm.sendMessage(companyDO, messageApi);
                }
            });

        }
        //2、自动释放客户-数量超限的
        customerPoolAlgorithm.autoReleaseCustomer(traders, employeeNotices);

    }

    @Override
    public CustomerPoolScoreTimeConfigRspDTO getScoreTimeConfig(Date now) {
        return customerPoolAlgorithm.getScoreTimeConfig(now);
    }

    @SuppressWarnings("unchecked")
    @Override
    public PageRspDTO<CustomerPoolListRspDTO> list(PageReqDTO pageReqDTO, CustomerPoolListReqDTO reqDTO) {
        CompanyQO qo = new CompanyQO();
        BeanUtils.copyProperties(reqDTO, qo);
        PageDTOHelper.startPage(pageReqDTO);
        List<CompanyDO> companies = companyMapper.listBy(qo);
        if (CollUtil.isEmpty(companies)) {
            return PageRspDTO.EMPTY;
        }

        List<Long> companyIdList = companies.stream().map(CompanyDO::getId).collect(Collectors.toList());

        // 公司行业类型
        Map<Long, CompanyTypeRspDTO> companyTypeMap = companyIndustryTypeService.getTypesByCompanyIds(companyIdList);

        Map<Long, CompanyInformationDO> companyInfos = new HashMap<>();
        List<CompanyInformationDO> companyInformationDOS = companyInformationMapper.getByIds(companyIdList);
        if (!CollectionUtils.isEmpty(companyInformationDOS)) {
            companyInfos = companyInformationDOS
                    .stream()
                    .collect(Collectors.toMap(CompanyInformationDO::getId, e -> e));
        }

        PageRspDTO<CompanyDO> pageRspDTO = PageDTOHelper.toPageDTO(companies);
        Map<Long, List<CustomerPoolFollowDO>> follows = !companies.isEmpty() ?
                customerPoolFollowMapper.selectBy(
                                CustomerPoolFollowQO.builder()
                                        .companyIds(companies.stream().map(CompanyDO::getId).collect(Collectors.toList()))
                                        .followStatusList(Arrays.asList(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue(),
                                                CustomerFollowStatusEnum.FOLLOWED.getValue()))
                                        .orderBy("id desc").build())
                        .stream().collect(Collectors.groupingBy(CustomerPoolFollowDO::getCompanyId, Collectors.toList()))
                : Collections.emptyMap();
        Map<Long, CompanyInformationDO> finalCompanyInfos = companyInfos;
        List<CustomerPoolListRspDTO> rspDTOs = companies.stream()
                .sorted(Comparator.comparing(e -> !CollectionUtils.isEmpty(reqDTO.getIds()) ?
                        Long.valueOf(reqDTO.getIds().indexOf(e.getId())) : e.getId()))
                .map(e -> {
                    CustomerPoolListRspDTO rspDTO = new CustomerPoolListRspDTO();
                    BeanUtils.copyProperties(e, rspDTO);
                    rspDTO.setNoDevelopmentValueReason(finalCompanyInfos.containsKey(rspDTO.getId()) ? finalCompanyInfos.get(rspDTO.getId()).getNoDevelopmentValueReason() : 0);
                    rspDTO.setNoDevelopmentValueRemark(finalCompanyInfos.containsKey(rspDTO.getId()) ? finalCompanyInfos.get(rspDTO.getId()).getNoDevelopmentValueRemark() : "");
                    if (companyTypeMap.containsKey(e.getId())) {
                        rspDTO.setCompanyType(companyTypeMap.get(e.getId()).getCompanyType());
                        rspDTO.setCompanyTypeList(companyTypeMap.get(e.getId()).getCompanyTypeList());
                    }
                    for (CustomerPoolFollowDO follow : follows.getOrDefault(e.getId(), Collections.emptyList())) {
                        if (Objects.equals(follow.getFollowStatus(), CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                && rspDTO.getNextFollowTime() == null) {
                            rspDTO.setNextFollowTime(follow.getFollowTime());
                            rspDTO.setEffectiveTime(follow.getEffectiveTime());
                        } else if (Objects.equals(follow.getFollowStatus(), CustomerFollowStatusEnum.FOLLOWED.getValue())
                                && rspDTO.getLastFollowTime() == null) {
                            rspDTO.setLastFollowTime(follow.getActualFollowTime());
                        }
                    }
                    BeanUtilsWhmall.converDefaultTime(rspDTO);
                    return rspDTO;
                }).collect(Collectors.toList());
        return PageRspDTO.transfer(pageRspDTO, rspDTOs);
    }

    @Override
    public List<CustomerPoolListRspDTO> listByCompanyIds(List<Long> companyIds) {

        Assert.isTrue(CollUtil.isNotEmpty(companyIds), "请求参数缺失");

        List<CompanyDO> companyList = companyMapper.getByIds(companyIds);
        if (CollUtil.isEmpty(companyList)) {
            return Collections.emptyList();
        }

        // 公司行业类型
        Map<Long, CompanyTypeRspDTO> companyTypeMap = companyIndustryTypeService.getTypesByCompanyIds(companyIds);
        Map<Long, CompanyInformationDO> companyInformationMap = companyInformationMapper.getByIds(companyIds)
                .stream()
                .collect(Collectors.toMap(CompanyInformationDO::getId, e -> e));

        // 公司跟进数据
        Map<Long, List<CustomerPoolFollowDO>> follows = CollUtil.isNotEmpty(companyList)
                ? customerPoolFollowMapper.selectBy(CustomerPoolFollowQO
                        .builder()
                        .companyIds(companyList.stream().map(CompanyDO::getId).collect(Collectors.toList()))
                        .followStatusList(Arrays.asList(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue(),
                                CustomerFollowStatusEnum.FOLLOWED.getValue()))
                        .orderBy("id desc")
                        .build()).stream()
                .collect(Collectors.groupingBy(CustomerPoolFollowDO::getCompanyId, Collectors.toList()))
                : Collections.emptyMap();

        return companyList
                .stream()
                .sorted(Comparator.comparing(e -> CollUtil.isNotEmpty(companyIds)
                        ? Long.valueOf(companyIds.indexOf(e.getId()))
                        : e.getId()))
                .map(e -> {
                    CustomerPoolListRspDTO rspDTO = new CustomerPoolListRspDTO();
                    BeanUtils.copyProperties(e, rspDTO);
                    rspDTO.setNoDevelopmentValueReason(companyInformationMap.containsKey(rspDTO.getId()) ? companyInformationMap.get(rspDTO.getId()).getNoDevelopmentValueReason() : 0);
                    rspDTO.setNoDevelopmentValueRemark(companyInformationMap.containsKey(rspDTO.getId()) ? companyInformationMap.get(rspDTO.getId()).getNoDevelopmentValueRemark() : "");
                    if (companyTypeMap.containsKey(e.getId())) {
                        rspDTO.setCompanyType(companyTypeMap.get(e.getId()).getCompanyType());
                        rspDTO.setCompanyTypeList(companyTypeMap.get(e.getId()).getCompanyTypeList());
                    }
                    for (CustomerPoolFollowDO follow : follows.getOrDefault(e.getId(), Collections.emptyList())) {
                        if (Objects.equals(follow.getFollowStatus(), CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                && rspDTO.getNextFollowTime() == null) {
                            rspDTO.setNextFollowTime(follow.getFollowTime());
                            rspDTO.setEffectiveTime(follow.getEffectiveTime());
                        } else if (Objects.equals(follow.getFollowStatus(), CustomerFollowStatusEnum.FOLLOWED.getValue())
                                && rspDTO.getLastFollowTime() == null) {
                            rspDTO.setLastFollowTime(follow.getActualFollowTime());
                        }
                    }
                    BeanUtilsWhmall.converDefaultTime(rspDTO);
                    return rspDTO;
                }).collect(Collectors.toList());
    }


    @Override
    public Boolean chockfollowStatus(Long id) {
        List<CustomerPoolFollowDO> poolFollowDOList = customerPoolFollowMapper.selectBy(
                CustomerPoolFollowQO.builder()
                        .companyId(id)
                        .orderBy("id desc").build());
        if (!CollectionUtils.isEmpty(poolFollowDOList)) {
            CustomerPoolFollowDO poolFollowDO = poolFollowDOList.get(0);
            return poolFollowDO != null
                    &&
                    (poolFollowDO.getFollowStatus().equals(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue()) ||
                            poolFollowDO.getFollowStatus().equals(CustomerFollowStatusEnum.FOLLOWED.getValue()));
        }
        return false;

    }

    @Override
    public PageRspDTO<CustomerPoolLogListRspDTO> listLog(PageReqDTO pageReqDTO, Long companyId) {
        PageDTOHelper.startPage(pageReqDTO);
        List<CustomerPoolLogDO> logs = customerPoolLogMapper.selectBy(
                CustomerPoolLogQO.builder().companyId(companyId).orderBy("id desc").build());
        PageRspDTO<CustomerPoolLogDO> pageRspDTO = PageDTOHelper.toPageDTO(logs);
        List<CustomerPoolLogListRspDTO> rspDTOs = logs.stream().map(e -> {
            CustomerPoolLogListRspDTO rspDTO = new CustomerPoolLogListRspDTO();
            BeanUtils.copyProperties(e, rspDTO);
            rspDTO.setRemark("【" + Optional.ofNullable(CustomerPoolOperationTypeEnum.fromValue(rspDTO.getOperationType()))
                    .map(CustomerPoolOperationTypeEnum::getName)
                    .orElse("") +
                    "】" +
                    (StringUtils.hasText(e.getRemark()) ? e.getRemark() : ""));
            return rspDTO;
        }).collect(Collectors.toList());
        return PageRspDTO.transfer(pageRspDTO, rspDTOs);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void acquire(CustomerPoolAcquireReqDTO reqDTO) {
        Assert.isTrue(!CollectionUtils.isEmpty(reqDTO.getIds()), "参数错误");
        Map<String, Integer> customerGrades = new HashMap<>();

        companyMapper.getByIds(reqDTO.getIds()).forEach(e -> {
            if (reqDTO.getOperation() == 1) {
                Assert.isTrue(e.getTraderId() == 0L, "不在公共池，不能操作");
                // 检验非高校且级别是CDE时，锁定个人客户池不计算在内
                customerGrades.put(e.getCustomerGrade(), customerGrades.getOrDefault(e.getCustomerGrade(), 0) + 1);
            } else {
                Assert.isTrue(e.getIsLockPersonalPoolCustomer() == 0, "已锁定，不能操作");
            }
        });
        if (reqDTO.getOperation() == 1) {
            // 获取
            StatisticCustomerNumber4TraderDTO statistic = customerPoolAlgorithm.statisticTraderCustomerNumber(
                    reqDTO.getTraders()).getOrDefault(reqDTO.getEmployeeId(), new StatisticCustomerNumber4TraderDTO());
            int cCount = statistic.getCGradeNumber();
            int dCount = statistic.getDGradeNumber();
            // 分配客户数
            customerGrades.forEach(statistic::acquire);
            // 判断客户数
            Map<String, Integer> needRelease = statistic.isNeedRelease(null);
            if (!needRelease.isEmpty()) {
                // 存在超出的
                Assert.isFalse(needRelease.containsKey("ALL"),
                        "获取后，名下客户数为{}，已超客户上限数{}，请释放超限客户，时间内未释放，系统将随机释放客户",
                        statistic.getAll(),
                        needRelease.get("ALL"));
                // C或D超出
                Assert.isFalse(needRelease.containsKey("C") || needRelease.containsKey("D"),
                        "C类客户已有{}个 ，D类客户已有{}个，现在勾选C类客户{}个 ，D类客户{}个，则需要释放对应的等级的客户数后，再获取新的客户",
                        cCount,
                        dCount,
                        customerGrades.getOrDefault(CustomerGradeEnum.C.getValue(), 0),
                        customerGrades.getOrDefault(CustomerGradeEnum.D.getValue(), 0));
                // E超出
                Assert.isFalse(needRelease.containsKey("E"),
                        "获取后，名下E类客户数为{}，已超E类客户上限数{}，请释放超限客户，时间内未释放，系统将随机释放客户",
                        statistic.getEGradeNumber(),
                        needRelease.get("E"));
            }
        }
        Map<Long, CompanyDO> companyDOMap = companyMapper.getByIds(reqDTO.getIds()).stream().collect(Collectors.toMap(CompanyDO::getId, e -> e));


        EmployeeRspDTO employeeRspDTO = null;
        if (reqDTO.getOperation() != 1) {

            List<QueryManagerRspDTO> queryManagerRspDTOS = employeeApi.queryManagers(QueryManagerReqDTO.builder().build());
            List<Long> employees = queryManagerRspDTOS.stream().filter(e ->
                    e.getPower().equals(PowerEnum.TRADER.getValue())
                            && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                            && e.getIsCaocno()
            ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
            Long traderId = companyAlgorithm.getOneManager(PositionEnum.TRADER.getValue(), false, null, employees);
            employeeRspDTO = employeeApi.getById(traderId);
            Assert.notNull(employeeRspDTO, "员工" + traderId + "不存在");
        }


        companyMapper.updateByIds(
                CompanyQO.builder().ids(reqDTO.getIds())
                        .traderId(reqDTO.getOperation() == 1 ? reqDTO.getEmployeeId() :
                                Optional.ofNullable(employeeRspDTO).map(EmployeeRspDTO::getId).orElse(0L))
                        .traderDepartmentPath(reqDTO.getOperation() == 1 ?
                                reqDTO.getTraders().get(reqDTO.getEmployeeId()).getDepartmentPath() :
                                Optional.ofNullable(employeeRspDTO).map(EmployeeRspDTO::getDepartmentPath).orElse(""))
                        .acquisitionNumberIncrement(reqDTO.getOperation() == 1 ? 1 : 0)
                        .isCustomerPoolCustomer(reqDTO.getOperation() == 1 ? 0 : 1)
                        .isLockPersonalPoolCustomer(reqDTO.getOperation() == 1 ? null : 0)////移出个人客户池时解锁
                        .updateTime(new Date()).build());
        Map<Long, List<CustomerPoolFollowDO>> follows = customerPoolFollowMapper.selectBy(
                        CustomerPoolFollowQO.builder().companyIds(reqDTO.getIds())
                                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                .orderBy("id desc").build())
                .stream().collect(Collectors.groupingBy(CustomerPoolFollowDO::getCompanyId, Collectors.toList()));
        reqDTO.getIds().forEach(id -> {
            //释放开发客户栏
            if (reqDTO.getOperation() == 0 && follows.containsKey(id)) {
                follows.get(id).forEach(follow -> {
                    customerPoolFollowMapper.updateByIdSelective(
                            CustomerPoolFollowDO.builder().id(follow.getId())
                                    .followStatus(CustomerFollowStatusEnum.RELEASED.getValue())
                                    .updateTime(new Date()).build()
                    );
                    CustomerPoolFollowRecordDO record = new CustomerPoolFollowRecordDO();
                    BeanUtils.copyProperties(follow, record);
                    record.setId(null);
                    record.setTraderId(reqDTO.getEmployeeId());
                    record.setTraderDepartmentPath(reqDTO.getEmployeeDepartmentPath());
                    record.setFollowId(follow.getId());
                    record.setCreateTime(new Date());
                    record.setRemark("释放客户 " + reqDTO.getRemark());
                    customerPoolFollowRecordMapper.insert(record);
                });
            }
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(id)
                            .operationType(reqDTO.getOperation() == 1 ? CustomerPoolOperationTypeEnum.ACQUIRE_CUSTOMER.getValue()
                                    : CustomerPoolOperationTypeEnum.RELEASE_CUSTOMER.getValue())
                            .remark(reqDTO.getRemark())
                            .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                            .handleId(reqDTO.getEmployeeId())
                            .handleTime(new Date()).build()
            );

            //公司日志
            CompanyLogDO logDO = CompanyLogDO.builder()
                    .companyId(id)
                    .createType(OperatorTypeEnum.EMPLOYEE.getValue())
                    .createId(reqDTO.getEmployeeId())
                    .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                    .operationDesc1(reqDTO.getOperation() == 1 ?
                            "获取公司" : "释放公司"
                            + (StringUtils.hasText(reqDTO.getRemark()) ? "，原因“" + reqDTO.getRemark() + "”" : ""))
                    .ip(reqDTO.getIp()).createTime(new Date()).build();
            companyLogMapper.insertSelective(logDO);
            CompanyDO companyDO = companyDOMap.get(id);
            if (companyDO != null && reqDTO.getOperation() != 1) {
                messageApi.sendMsg(EmployeeNoticeDTO.builder()
                        .noticeCategory(NoticeCategoryConstant.REMIND)
                        .noticeType(EmployeeNoticeTypeEnum.RELEASE_CUSTOMER.getValue())
                        .noticeContentType(NoticeContentTypeConstant.TEXT)
                        .noticeOperationType(NoticeOperationTypeConstant.REDIRECT_TO_DETAIL)
                        .noticeOperationValue(String.format("{\"key\":\"%s\"}", companyDO.getId()))
                        .content(companyDO.getName() + "已被释放")
                        .relateNo(companyDO.getId().toString())
                        .employeeIds(Collections.singletonList(companyDO.getTraderId()))
                        .createId(reqDTO.getEmployeeId())
                        .createType(OperatorTypeEnum.EMPLOYEE.getValue())
                        .build());
            }
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createOrder(Long companyId) {
        if (companyId == null) {
            return;
        }
        CompanyDO company = companyMapper.selectById(companyId);
        if (company.getIsCustomerPoolCustomer() == 0) {
            return;
        }
        List<CustomerPoolFollowDO> follows = customerPoolFollowMapper.selectBy(CustomerPoolFollowQO.builder()
                .companyId(companyId)
                .followStatusList(Collections.singletonList(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue()))
                .build());
        // 将全部未跟进更新为已成单
        follows.forEach(e -> customerPoolFollowMapper.updateByIdSelective(
                CustomerPoolFollowDO.builder().id(e.getId())
                        .followStatus(CustomerFollowStatusEnum.HAS_ORDER.getValue())
                        .updateTime(new Date()).build()
        ));
        // 客户池日志增加
        customerPoolLogMapper.insertSelective(
                CustomerPoolLogDO.builder()
                        .companyId(companyId)
                        .operationType(CustomerPoolOperationTypeEnum.CREATE_ORDER.getValue())
                        .remark("公司从公共池释放")
                        .handleType(OperatorTypeEnum.SYSTEM.getValue())
                        .handleId(0L)
                        .handleTime(new Date()).build());
        // 增加公司日志
        companyLogMapper.insertSelective(CompanyLogDO.builder()
                .companyId(companyId)
                .createType(OperatorTypeEnum.SYSTEM.getValue())
                .createId(0L)
                .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                .operationDesc1("创建订单，公司从公共池释放")
                .createTime(new Date())
                .ip("")
                .build());
        if (company.getTraderId() > 0) {
            companyMapper.updateByIds(
                    CompanyQO.builder().ids(Collections.singletonList(companyId))
                            .isCustomerPoolCustomer(0)
                            //移出个人客户池时解锁
                            .isLockPersonalPoolCustomer(0)
                            .updateTime(new Date()).build());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void assign(CustomerPoolAssignReqDTO reqDTO) {
        Assert.isTrue(!CollectionUtils.isEmpty(reqDTO.getIds()), "参数错误");
        List<Long> ids = new ArrayList<>(), idsHasTrader = new ArrayList<>();
        List<CompanyDO> companies = companyMapper.getByIds(reqDTO.getIds());
        companies.forEach(e -> {
            Assert.isTrue(e.getIsCustomerPoolCustomer() == 1, "不在客户池，不能操作");
            if (e.getTraderId() != reqDTO.getTraderId().longValue()) {
                ids.add(e.getId());
            }
            if (e.getTraderId() > 0) {
                idsHasTrader.add(e.getId());
            }
        });
        Map<Long, CompanyDO> companyMap = companies.stream().collect(Collectors.toMap(CompanyDO::getId, e -> e));
        if (ids.isEmpty()) {
            return;
        }
        //释放跟进
        customerPoolFollowMapper.selectBy(
                        CustomerPoolFollowQO.builder().companyIds(ids)
                                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                .build())
                .forEach(e -> customerPoolFollowMapper.updateByIdSelective(
                        CustomerPoolFollowDO.builder().id(e.getId())
                                .followStatus(CustomerFollowStatusEnum.RELEASED.getValue())
                                .updateTime(new Date()).build()
                ));
        //分配撮合
        companyMapper.updateByIds(
                CompanyQO.builder().ids(ids)
                        .traderId(reqDTO.getTraderId())
                        .traderDepartmentPath(reqDTO.getTraderDepartmentPath())
                        .isCustomerPoolCustomer(0)
                        .updateTime(new Date()).build());
        ids.forEach(id -> {
            //记录日志
            if (idsHasTrader.contains(id)) {
                customerPoolLogMapper.insertSelective(
                        CustomerPoolLogDO.builder()
                                .companyId(id)
                                .operationType(CustomerPoolOperationTypeEnum.RELEASE_CUSTOMER.getValue())
                                .remark("分配客户时先释放到公共池")
                                .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                                .handleId(reqDTO.getEmployeeId())
                                .handleTime(new Date()).build()
                );
            }
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(id)
                            .operationType(CustomerPoolOperationTypeEnum.ASSIGN_CUSTOMER.getValue())
                            .remark(null)
                            .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                            .handleId(reqDTO.getEmployeeId())
                            .handleTime(new Date()).build()
            );
            if (reqDTO.getNameMap() != null && companyMap.get(id) != null) {
                //添加公司操作日志
                CompanyLogDO logDO = CompanyLogDO.builder()
                        .companyId(id)
                        .createType(OperatorTypeEnum.EMPLOYEE.getValue())
                        .createId(reqDTO.getEmployeeId())
                        .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                        .operationDesc1("分配客户将公司撮合由" + reqDTO.getNameMap().get(companyMap.get(id).getTraderId()) + "更改为" + reqDTO.getTraderName())
                        .ip(null).createTime(new Date()).build();
                companyLogMapper.insertSelective(logDO);
            }
        });

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void push(CustomerPoolPushReqDTO reqDTO) {
        Assert.isTrue(!CollectionUtils.isEmpty(reqDTO.getIds()), "参数错误");
        List<CompanyDO> companies = companyMapper.getByIds(reqDTO.getIds());
        companies.forEach(e -> {
            Assert.isTrue(e.getIsCustomerPoolCustomer() == 1, "不在客户池，不能操作");
            Assert.isTrue(e.getTraderId() > 0, "客户没有撮合，不能操作");
        });
        Map<Long, CompanyDO> companyMap = companies.stream().collect(Collectors.toMap(CompanyDO::getId, e -> e));
        //释放跟进
        customerPoolFollowMapper.selectBy(
                        CustomerPoolFollowQO.builder().companyIds(reqDTO.getIds())
                                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                .build())
                .forEach(e -> customerPoolFollowMapper.updateByIdSelective(
                        CustomerPoolFollowDO.builder().id(e.getId())
                                .followStatus(CustomerFollowStatusEnum.RELEASED.getValue())
                                .updateTime(new Date()).build()
                ));
        //新增跟进
        reqDTO.getIds().forEach(id -> {
            CustomerPoolFollowDO follow = CustomerPoolFollowDO.builder().sourceId(0L)
                    .companyId(id)
                    .traderId(companyMap.get(id).getTraderId())
                    .traderDepartmentPath(companyMap.get(id).getTraderDepartmentPath())
                    .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                    .followTime(reqDTO.getFollowTime())
                    .effectiveTime(reqDTO.getEffectiveTime())
                    .createTime(new Date()).build();
            customerPoolFollowMapper.insertSelective(follow);
            customerPoolFollowMapper.updateByIdSelective(
                    CustomerPoolFollowDO.builder().id(follow.getId()).rootId(follow.getId()).build()
            );
            CustomerPoolFollowRecordDO record = new CustomerPoolFollowRecordDO();
            BeanUtils.copyProperties(follow, record);
            record.setId(null);
            record.setFollowId(follow.getId());
            record.setCreateTime(new Date());
            record.setRemark("推送开发客户栏");
            customerPoolFollowRecordMapper.insert(record);
            //记录日志
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(id)
                            .operationType(CustomerPoolOperationTypeEnum.ENTER_CUSTOMER_POOL.getValue())
                            .remark(null)
                            .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                            .handleId(reqDTO.getEmployeeId())
                            .handleTime(new Date()).build()
            );
            CompanyLogDO logDO = CompanyLogDO.builder()
                    .companyId(id)
                    .createType(OperatorTypeEnum.EMPLOYEE.getValue())
                    .createId(reqDTO.getEmployeeId())
                    .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                    .operationDesc1("推送客户到开发客户栏")
                    .ip(null).createTime(new Date()).build();
            companyLogMapper.insertSelective(logDO);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void hide(UpdateBatchCommonReqDTO reqDTO) {
        Assert.isTrue(!CollectionUtils.isEmpty(reqDTO.getIds()), "参数错误");

        Map<Long, CompanyInformationDO> companyInfos = companyInformationMapper.getByIds(reqDTO.getIds())
                .stream().collect(Collectors.toMap(CompanyInformationDO::getId, e -> e));

        // 需要设置为显示的公司数据
        reqDTO.getIds()
                .forEach(id -> {

                    // 是否操作db
                    boolean dbFlag = false;

                    List<Long> hiddenEmployeeIds = FieldUtils.unwrapArray(companyInfos.get(id).getHiddenIdList(), "-", Long.class);
                    if (reqDTO.getOperation().equals(YesNoEnum.YES.getCode())) {
                        // 设置隐藏
                        if (!hiddenEmployeeIds.contains(reqDTO.getEmployeeId())) {
                            hiddenEmployeeIds.add(reqDTO.getEmployeeId());
                            dbFlag = true;
                        }
                    } else {
                        // 移除隐藏
                        if (hiddenEmployeeIds.contains(reqDTO.getEmployeeId())) {
                            hiddenEmployeeIds = hiddenEmployeeIds
                                    .stream()
                                    .filter(eid -> !reqDTO.getEmployeeId().equals(eid))
                                    .collect(Collectors.toList());
                            dbFlag = true;
                        }
                    }

                    if (dbFlag) {
                        CompanyInformationDO updateInfoDo = new CompanyInformationDO();
                        updateInfoDo.setId(id);
                        updateInfoDo.setHiddenIdList(FieldUtils.wrapArray(hiddenEmployeeIds, "-", true));
                        companyInformationMapper.updateByIdSelective(updateInfoDo);
                        customerPoolLogMapper.insertSelective(
                                CustomerPoolLogDO.builder()
                                        .companyId(id)
                                        .operationType(reqDTO.getOperation().equals(YesNoEnum.YES.getCode())
                                                ? CustomerPoolOperationTypeEnum.HIDE_CUSTOMER.getValue()
                                                : CustomerPoolOperationTypeEnum.RESTORE_CUSTOMER.getValue())
                                        .remark(null)
                                        .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                                        .handleId(reqDTO.getEmployeeId())
                                        .handleTime(new Date())
                                        .build());
                    }
                });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void noDevelopmentValue(UpdateBatchCommonReqDTO reqDTO) {
        Assert.isTrue(!CollectionUtils.isEmpty(reqDTO.getIds()), "参数错误");
        Map<Long, CompanyDO> companies = companyMapper.getByIds(reqDTO.getIds())
                .stream().collect(Collectors.toMap(CompanyDO::getId, e -> e));
        companies.values().forEach(e -> {
            if (reqDTO.getOperation() == 0 && e.getTraderId() > 0) {
                Assert.isTrue(e.getTraderId() == reqDTO.getEmployeeId().longValue(), "当前撮合不是您，不能操作");
            }
        });
        companyMapper.updateByIds(
                CompanyQO.builder().ids(reqDTO.getIds())
                        .isDevelopmentValue(reqDTO.getOperation())
                        .traderId(reqDTO.getOperation() == 0 ? 0L : null)
                        .traderDepartmentPath(reqDTO.getOperation() == 0 ? "" : null)
                        .updateTime(new Date()).build());
        reqDTO.getIds().stream()
                .filter(id -> companies.get(id).getIsDevelopmentValue() != reqDTO.getOperation().intValue())
                .forEach(id -> customerPoolLogMapper.insertSelective(
                        CustomerPoolLogDO.builder()
                                .companyId(id)
                                .operationType(reqDTO.getOperation() == 0 ? CustomerPoolOperationTypeEnum.NO_DEVELOPMENT_VALUE.getValue()
                                        : CustomerPoolOperationTypeEnum.RESTORE_DEVELOPMENT_VALUE.getValue())
                                .remark(reqDTO.getRemark())
                                .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                                .handleId(reqDTO.getEmployeeId())
                                .handleTime(new Date()).build()
                ));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lock(UpdateCommonReqDTO reqDTO) {
        companyMapper.updateByIds(
                CompanyQO.builder().ids(Collections.singletonList(reqDTO.getId()))
                        .isLockPersonalPoolCustomer(reqDTO.getOperation())
                        .updateTime(new Date()).build());

        //添加公司操作日志
        String remark = reqDTO.getOperation() == 1 ? "锁定客户：" : "解锁客户";
        CompanyLogDO logDO = CompanyLogDO.builder()
                .companyId(reqDTO.getId())
                .createType(OperatorTypeEnum.EMPLOYEE.getValue())
                .createId(reqDTO.getEmployeeId())
                .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                .operationDesc1(remark + reqDTO.getRemark())
                .ip(null).createTime(new Date()).build();
        companyLogMapper.insertSelective(logDO);
        customerPoolLogMapper.insertSelective(
                CustomerPoolLogDO.builder()
                        .companyId(reqDTO.getId())
                        .operationType(reqDTO.getOperation() == 1 ? CustomerPoolOperationTypeEnum.LOCK_CUSTOMER.getValue()
                                : CustomerPoolOperationTypeEnum.UNLOCK_CUSTOMER.getValue())
                        .remark(reqDTO.getRemark())
                        .handleType(OperatorTypeEnum.EMPLOYEE.getValue())
                        .handleId(reqDTO.getEmployeeId())
                        .handleTime(new Date()).build()
        );
    }

    @Override
    public PageRspDTO<CustomerPoolListFollowRspDTO> listFollow(PageReqDTO pageReqDTO, CustomerPoolListFollowReqDTO reqDTO) {
        CustomerPoolFollowQO qo = new CustomerPoolFollowQO();
        BeanUtils.copyProperties(reqDTO, qo);
        qo.setOrderBy("id desc");
        PageDTOHelper.startPage(pageReqDTO);
        List<CustomerPoolFollowDO> follows = customerPoolFollowMapper.selectBy(qo);
        PageRspDTO<CustomerPoolFollowDO> pageRspDTO = PageDTOHelper.toPageDTO(follows);
        //查询最新跟进记录
        qo = new CustomerPoolFollowQO();
        qo.setCompanyIds(follows.stream().map(CustomerPoolFollowDO::getCompanyId).collect(Collectors.toList()));
        qo.setOrderBy("id desc");
        List<CustomerPoolFollowDO> followsAllList = !follows.isEmpty() ?
                customerPoolFollowMapper.selectBy(qo) : Collections.emptyList();
        Map<Long, List<CustomerPoolFollowDO>> followsAllMap = followsAllList.stream()
                .collect(Collectors.groupingBy(CustomerPoolFollowDO::getSourceId, Collectors.toList()));
        Map<Long, List<CustomerPoolFollowRecordDO>> records = !followsAllList.isEmpty() ?
                customerPoolFollowRecordMapper.selectBy(
                                CustomerPoolFollowRecordQO.builder()
                                        .followIds(followsAllList.stream().map(CustomerPoolFollowDO::getId).collect(Collectors.toList()))
                                        .businessType(CustomerPoolFollowBusinessTypeEnum.CUSTOMER_POOL_FOLLOW.getValue())
                                        .orderBy("id desc").build())
                        .stream().collect(Collectors.groupingBy(CustomerPoolFollowRecordDO::getFollowId, Collectors.toList()))
                : Collections.emptyMap();
        Map<Long, CompanyDO> companies = !follows.isEmpty() ?
                companyMapper.getByIds(follows.stream().map(CustomerPoolFollowDO::getCompanyId).collect(Collectors.toList()))
                        .stream().collect(Collectors.toMap(CompanyDO::getId, e -> e))
                : Collections.emptyMap();
        List<CustomerPoolListFollowRspDTO> rspDTOs = follows.stream().map(e -> {
            CustomerPoolListFollowRspDTO rspDTO = new CustomerPoolListFollowRspDTO();
            BeanUtils.copyProperties(e, rspDTO);
            rspDTO.setCompanyName(companies.get(e.getCompanyId()).getName());
            List<CustomerPoolFollowRecordDO> recordTemp = records.get(e.getId());
            if (recordTemp != null && !recordTemp.isEmpty()) {
                rspDTO.setRemark(recordTemp.get(0).getRemark());
            } else {
                rspDTO.setRemark("");
            }
            //顺藤摸瓜
            Long sourceId = e.getId();
            while (followsAllMap.containsKey(sourceId)) {
                rspDTO.setRemark(records.get(followsAllMap.get(sourceId).get(0).getId()).get(0).getRemark());
                sourceId = followsAllMap.get(sourceId).get(0).getId();
            }
            return rspDTO;
        }).collect(Collectors.toList());
        return PageRspDTO.transfer(pageRspDTO, rspDTOs);
    }

    @Override
    public PageRspDTO<CustomerPoolListMoreFollowRspDTO> listMoreFollow(PageReqDTO pageReqDTO, Long companyId) {
        PageDTOHelper.startPage(pageReqDTO);
        List<CustomerPoolFollowRecordDO> records = customerPoolFollowRecordMapper.selectBy(
                CustomerPoolFollowRecordQO.builder()
                        .companyId(companyId)
                        .businessType(CustomerPoolFollowBusinessTypeEnum.CUSTOMER_POOL_FOLLOW.getValue())
                        .orderBy("id desc").build());
        PageRspDTO<CustomerPoolFollowRecordDO> pageRspDTO = PageDTOHelper.toPageDTO(records);
        List<CustomerPoolListMoreFollowRspDTO> rspDTOs = records.stream().map(e -> {
            CustomerPoolListMoreFollowRspDTO rspDTO = new CustomerPoolListMoreFollowRspDTO();
            BeanUtils.copyProperties(e, rspDTO);
            rspDTO.setRemark(e.getRemark());
            return rspDTO;
        }).collect(Collectors.toList());
        return PageRspDTO.transfer(pageRspDTO, rspDTOs);
    }

    @Override
    public List<CustomerPoolExportFollowRspDTO> exportFollow(CustomerPoolExportFollowReqDTO reqDTO) {
        CustomerPoolFollowRecordQO qo = new CustomerPoolFollowRecordQO();
        BeanUtils.copyProperties(reqDTO, qo);
        qo.setCreateTimeLte(DateUtils.getDayEnd(qo.getCreateTimeLte()));
        qo.setBusinessType(CustomerPoolFollowBusinessTypeEnum.CUSTOMER_POOL_FOLLOW.getValue());
        List<CustomerPoolFollowRecordDO> records = customerPoolFollowRecordMapper.selectBy(qo);
        Map<Long, CustomerPoolFollowDO> follows = !records.isEmpty() ?
                customerPoolFollowMapper.selectBy(
                                CustomerPoolFollowQO.builder()
                                        .ids(records.stream().map(CustomerPoolFollowRecordDO::getFollowId).collect(Collectors.toList()))
                                        .build())
                        .stream().collect(Collectors.toMap(CustomerPoolFollowDO::getId, e -> e))
                : Collections.emptyMap();
        return records.stream().map(e -> {
            CustomerPoolExportFollowRspDTO rspDTO = new CustomerPoolExportFollowRspDTO();
            BeanUtils.copyProperties(e, rspDTO);
            rspDTO.setFollowStatusName(CustomerFollowStatusEnum.fromValue(follows.get(e.getFollowId()).getFollowStatus()).getName());
            rspDTO.setRemark(e.getRemark() + (FieldUtils.unwrapDate(e.getFollowTime()) != null ?
                    "。下次跟进时间" + DateUtils.formatDate(e.getFollowTime()) : ""));
            return rspDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<Date> listTimeDistri(CustomerPoolListTimeDistriReqDTO reqDTO) {
        CustomerPoolFollowQO qo = new CustomerPoolFollowQO();
        BeanUtils.copyProperties(reqDTO, qo);
        if (reqDTO.getFollowStatus() == null) {
            qo.setFollowStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue());
        }
        qo.setOrderBy("follow_time");
        return customerPoolFollowMapper.selectBy(qo)
                .stream().map(CustomerPoolFollowDO::getFollowTime)
                .distinct().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void follow(CustomerPoolFollowReqDTO reqDTO) {
        CustomerPoolFollowDO follow = customerPoolFollowMapper.selectById(reqDTO.getId());
        Assert.notNull(follow, "跟进【{}}】不存在", reqDTO.getId());
        Assert.isTrue(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue().equals(follow.getFollowStatus()),
                "跟进【{}】不是待跟进，无法跟进", reqDTO.getId());
        // 判断公司
        CompanyDO company = companyMapper.selectById(follow.getCompanyId());
        Assert.isTrue(company.getTraderId() != 0L, "撮合不是您，不能操作");
        Assert.isTrue(reqDTO.getFollowTime().before(DateUtils.addDays(DateUtils.getDayBegin(new Date()), 16)),
                "距离上次跟进时间不能超过15天");
        Assert.isTrue(follow.getTraderId() == reqDTO.getEmployeeId().longValue(), "撮合不是您，不能操作");
        CustomerPoolFollowDO followNew = new CustomerPoolFollowDO();
        BeanUtils.copyProperties(follow, followNew);
        followNew.setId(null);
        followNew.setSourceId(follow.getId());
        followNew.setFollowTime(reqDTO.getFollowTime());
        followNew.setEffectiveTime(reqDTO.getEffectiveTime());
        followNew.setFollowStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue());
        followNew.setCreateTime(new Date());

        // 此处查询是否已存在跟进记录
        Long l = customerPoolFollowMapper.selectCountBy(CustomerPoolFollowQO.builder()
                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                .companyId(followNew.getCompanyId())
                .traderId(followNew.getTraderId())
                .build());

        Assert.isTrue(l == 1L, "已存在{}条跟进记录，不要重复跟进", l);


        customerPoolFollowMapper.insertSelective(followNew);
        CustomerPoolFollowRecordDO recordNew = new CustomerPoolFollowRecordDO();
        BeanUtils.copyProperties(followNew, recordNew);
        recordNew.setId(null);
        recordNew.setFollowId(followNew.getId());
        recordNew.setCreateTime(new Date());
        recordNew.setRemark(reqDTO.getRemark());
        recordNew.setHasEecTime(reqDTO.getHasEecTime());
        recordNew.setHasApContract(reqDTO.getHasApContract());
        customerPoolFollowRecordMapper.insert(recordNew);
        customerPoolFollowMapper.updateByIdSelective(
                CustomerPoolFollowDO.builder().id(follow.getId())
                        .followStatus(CustomerFollowStatusEnum.FOLLOWED.getValue())
                        .actualFollowTime(new Date())
                        .updateTime(new Date()).build()
        );
    }

    @Override
    public Long count(CustomerPoolListTimeDistriReqDTO condition) {
        return customerPoolFollowMapper.selectCountBy(CustomerPoolFollowQO.builder()
                .traderId(condition.getTraderId())
                .traderDepartmentPathPrefixLike(condition.getTraderDepartmentPathPrefixLike())
                .effectiveTime(condition.getEffectiveTime())
                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                .build());
    }
}
